GPS data
General Maps
# This piece of code is only there to show how to draw a polylines with a
# gradient color using leaflet. We're not using it due to the size of the
# created map, and will continue using circle marker
# datasets used to display map
df_driftrate <- unique(data_2018_filter[
.id == "ind_2018070" &
divetype == "2: drift",
.(.id, lat, lon, dduration)
])
# color palette
pal <- colorNumeric(
palette = "YlGnBu",
domain = df_driftrate$dduration
)
# add
df_driftrate[, `:=`(
nextLat = shift(lat),
nextLon = shift(lon),
color = pal(df_driftrate$dduration)
)]
# interactive map
gradient_map <- leaflet() %>%
setView(lng = -122, lat = 38, zoom = 2) %>%
addTiles()
# add lines
for (i in 1:nrow(df_driftrate)) {
gradient_map <- addPolylines(
map = gradient_map,
data = df_driftrate,
lat = as.numeric(df_driftrate[i, c("lat", "nextLat")]),
lng = as.numeric(df_driftrate[i, c("lon", "nextLon")]),
color = df_driftrate[i, color],
weight = 3,
group = "individual_1"
)
}
# add layer control
gradient_map <- addLayersControl(
map = gradient_map,
overlayGroups = c("individual_1"),
options = layersControlOptions(collapsed = FALSE)
)
Because for some data the contrast in changes was not enough marked, the only treatment applied on these data was to remove outliers for each variable using the interquartile range rule.
# interactive map
gradient_map <- leaflet() %>%
setView(lng = -132, lat = 48, zoom = 4) %>%
addTiles()
# loop by individuals and variable
grps <- NULL
for (i in data_2018_filter[!is.na(lat), unique(.id)]) {
for (k in c("dduration", "driftrate")) {
if (k == "driftrate") {
# set dataset used to plot
dataPlot <- unique(data_2018_filter %>%
.[order(date), ] %>%
.[
.id == i &
divetype == "2: drift" &
!is.na(get(k)),
c("lat", "lon", k),
with = FALSE
] %>%
.[!is_outlier(get(k)), ])
# color palette creation
colPal <- colorNumeric(
palette = "BrBG",
domain = seq(
-dataPlot[, max(abs(driftrate))],
dataPlot[, max(abs(driftrate))],
0.1
)
)
} else {
# set dataset used to plot
dataPlot <- unique(data_2018_filter %>%
.[order(date), ] %>%
.[
.id == i &
divetype != "2: drift" &
!is.na(get(k)),
c("lat", "lon", k),
with = FALSE
] %>%
.[!is_outlier(get(k)), ])
# color palette creation
colPal <- colorNumeric(
palette = "YlGnBu",
domain = dataPlot[, get(k)]
)
}
# add color to dataset
dataPlot[, color := colPal(dataPlot[, get(k)])]
# add size column
dataPlot[, radius := 3]
# mark the beginning of the trip
dataPlot[1, `:=`(
color = "green",
radius = 4
)]
# mark the end of the trip
dataPlot[.N, `:=`(
color = "red",
radius = 4
)]
# reorder to make the end and the beginning in front
dataPlot <- rbind(dataPlot[-1, ], dataPlot[1, ])
# convert to sf
dataPlot <- sf::st_as_sf(dataPlot, coords = c("lon", "lat"), crs = 4326)
# add markers to map
gradient_map <- addGlPoints(
map = gradient_map,
data = dataPlot,
radius = dataPlot$radius * 4,
stroke = FALSE,
fillColor = ~color,
group = paste(i, "-", k)
) %>%
addLegend("bottomleft",
data = dataPlot,
group = paste(i, "-", k),
pal = colPal,
values = ~ get(k),
title = k
)
# retrieve groups
grps <- c(grps, paste(i, "-", k))
}
}
# add layer control
gradient_map <- addLayersControl(
map = gradient_map,
overlayGroups = grps,
options = layersControlOptions(collapsed = TRUE)
) %>% hideGroup(grps)
# display
gradient_map
# clear memory
gc()
## used (Mb) gc trigger (Mb) max used (Mb)
## Ncells 5992819 320.1 10337201 552.1 10337201 552.1
## Vcells 20301471 154.9 50145364 382.6 50144451 382.6
rm(
gradient_map,
dataPlot
)
gc()
## used (Mb) gc trigger (Mb) max used (Mb)
## Ncells 5987785 319.8 10337201 552.1 10337201 552.1
## Vcells 19626803 149.8 50145364 382.6 50144451 382.6
Current Velocity Map
First we need to load current data. Here we loaded data from Copernicus platform using the product Global Ocean Physics Reanalysis that allow to get an estimation of the current velocity and eddies at the surface at a spatial resolution of 0.083° × 0.083° every day.
This step might take time since that represents a lot of data.
# import the already pre-treated ncdf
data("data_cop", package = "ontodive")
Then we can build the animation for each individual. The code below is not executed when this document is compiled, since it’s quite time consuming.
# easier (it's also because it was the only way that works) using a function
anim_plot_current <- function(data, id_inter) {
# plot
ggplot() +
geom_raster(
data = data_cop[time %between% c(
data[
.id == id_inter,
min(as.Date(date))
],
data[
.id == id_inter,
max(as.Date(date))
]
) &
longitude %between% c(
data[
.id == id_inter,
min(lon)
] - 2,
data[
.id == id_inter,
max(lon)
] + 2
) &
latitude %between% c(
data[
.id == id_inter,
min(lat)
] - 2,
data[
.id == id_inter,
max(lat)
] + 2
)],
aes(x = longitude, y = latitude, fill = vel),
interpolate = TRUE
) +
geom_point(
data = unique(data[
.id == id_inter,
.(
time = as.Date(date),
lat,
lon
)
]),
aes(x = lon, y = lat),
color = "white"
) +
geom_sf(
data = spData::world,
col = 1,
fill = "ivory"
) +
coord_sf(
xlim = c(
data[.id == id_inter, min(lon)] - 2,
data[.id == id_inter, max(lon)] + 2
),
ylim = c(
data[.id == id_inter, min(lat)] - 2,
data[.id == id_inter, max(lat)] + 2
)
) +
theme_jjo() +
labs(x = NULL, y = NULL) +
scale_fill_gradientn(
colours = oce::oceColors9A(120),
limits = c(0, 1)
) +
labs(title = paste(id_inter, "- Date: {frame_time}")) +
transition_time(time)
}
# apply this function to all individuals
res_ggplot <- lapply(data_2018_filter[!is.na(lat), unique(.id)],
anim_plot_current,
data = data_2018_filter
)
# apply the animation for each individual
res_gganimate <- lapply(res_ggplot, function(x) {
animate(x, renderer = magick_renderer())
})
# then to display the first individuals
res_gganimate[[1]]
# save the plot
anim_save("ind_2018070_vel_alltrip.gif", animation = last_animation())
Water Height Map
Another approach is to look at that with a view centered on the animal (I find it easier to spot any relation with the animal’s track and environmental conditions). Let’s have a look at another variable, the sea surface above geoid (often called SSH for Sea Surface Height, but called zos hereafter), that can be used to identify eddies.
Same as above, since this step is time-consuming, the following code will not be run, but gives you an idea of how to generate *.gif.
# get the position of the animal each day
gps_day <- data_2018_filter[!is.na(lat), .(date, lat, lon, .id)] %>%
.[, .(
lat = mean(lat, na.rm = T),
lon = mean(lon, na.rm = T)
),
by = .(.id, time = as.Date(date))
]
anim_plot_zos_center <- function(id_inter) {
# initiate
df_raster_inter <- data.table()
# example with id_inter
for (i in 1:gps_day[.id == id_inter, .N]) {
# retrieve the right values
time_inter <- gps_day[.id == id_inter, time][i]
lat_inter <- gps_day[.id == id_inter, lat][i]
lon_inter <- gps_day[.id == id_inter, lon][i]
# get the right data
df_raster_inter <- rbind(
df_raster_inter,
data_cop[time == time_inter &
latitude %between% c(
lat_inter - 4,
lat_inter + 4
) &
longitude %between% c(
lon_inter - 4,
lon_inter + 4
)]
)
}
# release memory
gc()
# plot
plot_animate <- ggplot() +
geom_raster(
data = df_raster_inter[, .(longitude, latitude, zos, time)],
aes(x = longitude, y = latitude, fill = zos)
) +
geom_path(
data = unique(data_2018_filter[.id == id_inter, .(lat, lon)]),
aes(x = lon, y = lat),
color = "red",
size = 2
) +
geom_point(
data = gps_day[.id == id_inter, ],
aes(x = lon, y = lat),
color = "white",
size = 2
) +
theme_jjo() +
labs(x = NULL, y = NULL) +
scale_fill_gradientn(colours = oce::oceColors9A(120)) +
labs(title = paste(id_inter, "- Date: {frame_time}")) +
transition_time(time) +
view_follow(exclude_layer = 2)
# rm
rm(df_raster_inter)
# return
return(plot_animate)
}
# apply this function to all individuals
res_ggplot_center <- lapply(
data_2018_filter[!is.na(lat), unique(.id)],
anim_plot_zos_center
)
# apply the animation for each individual
res_gganimate_center <- lapply(res_ggplot_center, function(x) {
animate(x, duration = 20, nframes = 200, renderer = magick_renderer())
gc()
})
# first individual
res_gganimate_center[[1]]
# save gif file
anim_save("ind_2018070_zos_center.gif", animation = last_animation())
In addition, using gganimate to generate *.gif file was found to be memory consuming so we had to developed for some individuals another way to generate *.gif file. Here we used the “old-fashioned” way consisting in generating all the plots and then compile them into a *.gif file.
another_anim_plot_zos_center <- function(id_inter) {
# example with id_inter
for (i in 1:gps_day[.id == id_inter, .N]) {
# retrieve the right values
time_inter <- gps_day[.id == id_inter, time][i]
lat_inter <- gps_day[.id == id_inter, lat][i]
lon_inter <- gps_day[.id == id_inter, lon][i]
# get the right data
df_raster_inter <- data_cop[time == time_inter &
latitude %between% c(
lat_inter - 4,
lat_inter + 4
) &
longitude %between% c(
lon_inter - 4,
lon_inter + 4
)]
# plot
p <- ggplot() +
geom_tile(
data = df_raster_inter[, .(longitude, latitude, zos, time)],
aes(x = longitude, y = latitude, fill = zos)
) +
geom_path(
data = unique(data_2018_filter[.id == id_inter, .(lat, lon)]),
aes(x = lon, y = lat),
color = "red",
size = 1.5
) +
geom_point(
data = gps_day[.id == id_inter, ][i, ],
aes(x = lon, y = lat),
color = "white",
size = 2
) +
theme_jjo() +
theme(axis.text.y = element_text(angle = 90)) +
labs(x = NULL, y = NULL) +
scale_fill_gradientn(colours = oce::oceColors9A(120)) +
labs(title = paste(id_inter, "- Date: ", time_inter)) +
coord_cartesian(
ylim = c(lat_inter - 4, lat_inter + 4),
xlim = c(lon_inter - 4, lon_inter + 4)
)
# save
ggsave(
plot = p,
filename = paste0("./tmp/", id_inter, " - Date: ", time_inter, ".png"),
device = "png"
)
}
}
# run the function for one individual
another_anim_plot_zos_center("ind_2018072")
# compile the gif file
gifski(
list.files("tmp/", pattern = "png$", full.names = T),
gif_file = "ind_2018072_zos_center.gif",
width = 480,
height = 480,
delay = 0.1
)
Hard to tell if there is any relation, we might need to dig deeper, especially for ind_2018072 and ind_2018080 by looking at the current direction and/or the distribution and abundance of different trophic level using SEAPODYM.
LS0tCnRpdGxlOiAiRGF0YSBFeHBsb3JhdGlvbiBORVMgKDIvMikgLSAyMDE4IgphdXRob3I6ICJKb2ZmcmV5IEpPVU1BQSIKZGF0ZTogImByIGludmlzaWJsZShTeXMuc2V0bG9jYWxlKGxvY2FsZSA9ICdDJykpOyBmb3JtYXQoU3lzLkRhdGUoKSwgZm9ybWF0ID0gJyVCICVkLCAlWScpYCIKb3V0cHV0OgogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIGNzczogY29zbW9fY3VzdG9tLmNzcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IGRlZmF1bHQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB5ZXMKICAgICAgc21vb3RoX3Njcm9sbDogbm8KbGluay1jaXRhdGlvbnM6IHllcwpwa2dkb3duOgogIGFzX2lzOiBmYWxzZQp2aWduZXR0ZTogPgogICVcVmlnbmV0dGVJbmRleEVudHJ5e05vcnRoZXJuIEVsZXBoYW50IFNlYWxzIC0gMjAxOCAoMi8yKX0KICAlXFZpZ25ldHRlRW5naW5le2tuaXRyOjpybWFya2Rvd259CiAgJVxWaWduZXR0ZUVuY29kaW5ne1VURi04fQotLS0KCiAgCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIGNvbW1hbmQgdG8gYnVpbGQgcGFja2FnZSB3aXRob3V0IGdldHRpbmcgdmlnbmV0dGUgZXJyb3IKIyBodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby9yZW52L2lzc3Vlcy84MzMKIyBkZXZ0b29sczo6Y2hlY2soYnVpbGRfYXJncz1jKCItLW5vLWJ1aWxkLXZpZ25ldHRlcyIpKQoKIyByZWR1Y2UgcG5nIHNpemUKIyBrbml0cjo6a25pdF9ob29rcyRzZXQob3B0aXBuZyA9IGtuaXRyOjpob29rX29wdGlwbmcpCiMga25pdHI6OmtuaXRfaG9va3Mkc2V0KHBuZ3F1YW50ID0ga25pdHI6Omhvb2tfcG5ncXVhbnQpCgojIGdsb2JhbCBvcHRpb24gcmVsYXRpdmUgdG8gcm1hcmtkb3duCmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBjYWNoZSA9IEZBTFNFLAogIGVjaG8gPSBUUlVFLAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogIG91dC53aWR0aCA9ICIxMDAlIiwKICBtZXNzYWdlID0gRkFMU0UsCiAgd2FybmluZyA9IEZBTFNFLAogICMgdGlkeSA9IFRSVUUsCiAgY2FjaGUubGF6eSA9IEZBTFNFLAogIG9wdGlwbmcgPSAiLW83IC1xdWlldCIsCiAgcG5ncXVhbnQgPSAiLS1zcGVlZD0xIgopCgojIGxpYnJhcnkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShsZWFmZ2wpCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShndHN1bW1hcnkpCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkoZ2djb3JycGxvdCkKbGlicmFyeShnZ25ld3NjYWxlKQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShnZW9zcGhlcmUpCmxpYnJhcnkodGlkeW5jKQpsaWJyYXJ5KGdnYW5pbWF0ZSkKbGlicmFyeSh0cmFuc2Zvcm1yKQpsaWJyYXJ5KG1hZ2ljaykKbGlicmFyeShnaWZza2kpCmxpYnJhcnkob250b2RpdmUpCgojIHJlbW92ZSBzb21lIHdhcm5pbmdzCnN1cHByZXNzV2FybmluZ3MobGlicmFyeShnZ3Bsb3QyKSkKCiMgZGVmaW5lIG15IG93biB0YWJsZSBmb3JtYXQ6IGh0dHBzOi8vZ2l0aHViLmNvbS9oYW96aHUyMzMva2FibGVFeHRyYS9pc3N1ZXMvMzc0CnNhYmxlIDwtIGZ1bmN0aW9uKHgsIGVzY2FwZSA9IFQsIC4uLikgewogIGtuaXRyOjprYWJsZSh4LCBlc2NhcGUgPSBlc2NhcGUsIC4uLikgJT4lCiAgICBrYWJsZV9zdHlsaW5nKAogICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAicmVzcG9uc2l2ZSIpLAogICAgICBmdWxsX3dpZHRoID0gRgogICAgKQp9CmBgYAoKYGBge3J9CiMgbG9hZCBsaWJyYXJ5CmxpYnJhcnkob250b2RpdmUpCgojIGxvYWQgZGF0YQpkYXRhX25lcyA8LSBnZXRfZGF0YSgibmVzIikKCiMgcmJpbmQgdGhlIGxpc3QKZGF0YV8yMDE4IDwtIHJiaW5kbGlzdChkYXRhX25lcyR5ZWFyXzIwMTgpCgojIGZpbHRlciBkYXRhCmRhdGFfMjAxOF9maWx0ZXIgPC0gZGF0YV8yMDE4W2RkdXJhdGlvbiA8IDMwMDAsIF0KYGBgCgojIyBHUFMgZGF0YQoKIyMjIEdlbmVyYWwgTWFwcwoKYGBge3IgZGF0YS1leHBsb3JhdGlvbi0yMDE4LTQzLCBmaWcuY2FwPSJNYXAgd2l0aCBwb2x5bGluZXMiLCBmaWcud2lkdGg9OCwgZXZhbD1GQUxTRX0KIyBUaGlzIHBpZWNlIG9mIGNvZGUgaXMgb25seSB0aGVyZSB0byBzaG93IGhvdyB0byBkcmF3IGEgcG9seWxpbmVzIHdpdGggYQojIGdyYWRpZW50IGNvbG9yIHVzaW5nIGxlYWZsZXQuIFdlJ3JlIG5vdCB1c2luZyBpdCBkdWUgdG8gdGhlIHNpemUgb2YgdGhlCiMgY3JlYXRlZCBtYXAsIGFuZCB3aWxsIGNvbnRpbnVlIHVzaW5nIGNpcmNsZSBtYXJrZXIKCiMgZGF0YXNldHMgdXNlZCB0byBkaXNwbGF5IG1hcApkZl9kcmlmdHJhdGUgPC0gdW5pcXVlKGRhdGFfMjAxOF9maWx0ZXJbCiAgLmlkID09ICJpbmRfMjAxODA3MCIgJgogICAgZGl2ZXR5cGUgPT0gIjI6IGRyaWZ0IiwKICAuKC5pZCwgbGF0LCBsb24sIGRkdXJhdGlvbikKXSkKCiMgY29sb3IgcGFsZXR0ZQpwYWwgPC0gY29sb3JOdW1lcmljKAogIHBhbGV0dGUgPSAiWWxHbkJ1IiwKICBkb21haW4gPSBkZl9kcmlmdHJhdGUkZGR1cmF0aW9uCikKCiMgYWRkCmRmX2RyaWZ0cmF0ZVssIGA6PWAoCiAgbmV4dExhdCA9IHNoaWZ0KGxhdCksCiAgbmV4dExvbiA9IHNoaWZ0KGxvbiksCiAgY29sb3IgPSBwYWwoZGZfZHJpZnRyYXRlJGRkdXJhdGlvbikKKV0KCiMgaW50ZXJhY3RpdmUgbWFwCmdyYWRpZW50X21hcCA8LSBsZWFmbGV0KCkgJT4lCiAgc2V0VmlldyhsbmcgPSAtMTIyLCBsYXQgPSAzOCwgem9vbSA9IDIpICU+JQogIGFkZFRpbGVzKCkKCiMgYWRkIGxpbmVzCmZvciAoaSBpbiAxOm5yb3coZGZfZHJpZnRyYXRlKSkgewogIGdyYWRpZW50X21hcCA8LSBhZGRQb2x5bGluZXMoCiAgICBtYXAgPSBncmFkaWVudF9tYXAsCiAgICBkYXRhID0gZGZfZHJpZnRyYXRlLAogICAgbGF0ID0gYXMubnVtZXJpYyhkZl9kcmlmdHJhdGVbaSwgYygibGF0IiwgIm5leHRMYXQiKV0pLAogICAgbG5nID0gYXMubnVtZXJpYyhkZl9kcmlmdHJhdGVbaSwgYygibG9uIiwgIm5leHRMb24iKV0pLAogICAgY29sb3IgPSBkZl9kcmlmdHJhdGVbaSwgY29sb3JdLAogICAgd2VpZ2h0ID0gMywKICAgIGdyb3VwID0gImluZGl2aWR1YWxfMSIKICApCn0KCiMgYWRkIGxheWVyIGNvbnRyb2wKZ3JhZGllbnRfbWFwIDwtIGFkZExheWVyc0NvbnRyb2woCiAgbWFwID0gZ3JhZGllbnRfbWFwLAogIG92ZXJsYXlHcm91cHMgPSBjKCJpbmRpdmlkdWFsXzEiKSwKICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpCikKYGBgCgpCZWNhdXNlIGZvciBzb21lIGRhdGEgdGhlIGNvbnRyYXN0IGluIGNoYW5nZXMgd2FzIG5vdCBlbm91Z2ggbWFya2VkLCB0aGUgb25seSB0cmVhdG1lbnQgYXBwbGllZCBvbiB0aGVzZSBkYXRhIHdhcyB0byByZW1vdmUgb3V0bGllcnMgZm9yIGVhY2ggdmFyaWFibGUgdXNpbmcgdGhlIGludGVycXVhcnRpbGUgcmFuZ2UgcnVsZS4gCgpgYGB7ciBkYXRhLWV4cGxvcmF0aW9uLTIwMTgtNDR9CiMgaW50ZXJhY3RpdmUgbWFwCmdyYWRpZW50X21hcCA8LSBsZWFmbGV0KCkgJT4lCiAgc2V0VmlldyhsbmcgPSAtMTMyLCBsYXQgPSA0OCwgem9vbSA9IDQpICU+JQogIGFkZFRpbGVzKCkKCiMgbG9vcCBieSBpbmRpdmlkdWFscyBhbmQgdmFyaWFibGUKZ3JwcyA8LSBOVUxMCmZvciAoaSBpbiBkYXRhXzIwMThfZmlsdGVyWyFpcy5uYShsYXQpLCB1bmlxdWUoLmlkKV0pIHsKICBmb3IgKGsgaW4gYygiZGR1cmF0aW9uIiwgImRyaWZ0cmF0ZSIpKSB7CiAgICBpZiAoayA9PSAiZHJpZnRyYXRlIikgewogICAgICAjIHNldCBkYXRhc2V0IHVzZWQgdG8gcGxvdAogICAgICBkYXRhUGxvdCA8LSB1bmlxdWUoZGF0YV8yMDE4X2ZpbHRlciAlPiUKICAgICAgICAuW29yZGVyKGRhdGUpLCBdICU+JQogICAgICAgIC5bCiAgICAgICAgICAuaWQgPT0gaSAmCiAgICAgICAgICAgIGRpdmV0eXBlID09ICIyOiBkcmlmdCIgJgogICAgICAgICAgICAhaXMubmEoZ2V0KGspKSwKICAgICAgICAgIGMoImxhdCIsICJsb24iLCBrKSwKICAgICAgICAgIHdpdGggPSBGQUxTRQogICAgICAgIF0gJT4lCiAgICAgICAgLlshaXNfb3V0bGllcihnZXQoaykpLCBdKQogICAgICAjIGNvbG9yIHBhbGV0dGUgY3JlYXRpb24KICAgICAgY29sUGFsIDwtIGNvbG9yTnVtZXJpYygKICAgICAgICBwYWxldHRlID0gIkJyQkciLAogICAgICAgIGRvbWFpbiA9IHNlcSgKICAgICAgICAgIC1kYXRhUGxvdFssIG1heChhYnMoZHJpZnRyYXRlKSldLAogICAgICAgICAgZGF0YVBsb3RbLCBtYXgoYWJzKGRyaWZ0cmF0ZSkpXSwKICAgICAgICAgIDAuMQogICAgICAgICkKICAgICAgKQogICAgfSBlbHNlIHsKICAgICAgIyBzZXQgZGF0YXNldCB1c2VkIHRvIHBsb3QKICAgICAgZGF0YVBsb3QgPC0gdW5pcXVlKGRhdGFfMjAxOF9maWx0ZXIgJT4lCiAgICAgICAgLltvcmRlcihkYXRlKSwgXSAlPiUKICAgICAgICAuWwogICAgICAgICAgLmlkID09IGkgJgogICAgICAgICAgICBkaXZldHlwZSAhPSAiMjogZHJpZnQiICYKICAgICAgICAgICAgIWlzLm5hKGdldChrKSksCiAgICAgICAgICBjKCJsYXQiLCAibG9uIiwgayksCiAgICAgICAgICB3aXRoID0gRkFMU0UKICAgICAgICBdICU+JQogICAgICAgIC5bIWlzX291dGxpZXIoZ2V0KGspKSwgXSkKICAgICAgIyBjb2xvciBwYWxldHRlIGNyZWF0aW9uCiAgICAgIGNvbFBhbCA8LSBjb2xvck51bWVyaWMoCiAgICAgICAgcGFsZXR0ZSA9ICJZbEduQnUiLAogICAgICAgIGRvbWFpbiA9IGRhdGFQbG90WywgZ2V0KGspXQogICAgICApCiAgICB9CgogICAgIyBhZGQgY29sb3IgdG8gZGF0YXNldAogICAgZGF0YVBsb3RbLCBjb2xvciA6PSBjb2xQYWwoZGF0YVBsb3RbLCBnZXQoayldKV0KICAgICMgYWRkIHNpemUgY29sdW1uCiAgICBkYXRhUGxvdFssIHJhZGl1cyA6PSAzXQogICAgIyBtYXJrIHRoZSBiZWdpbm5pbmcgb2YgdGhlIHRyaXAKICAgIGRhdGFQbG90WzEsIGA6PWAoCiAgICAgIGNvbG9yID0gImdyZWVuIiwKICAgICAgcmFkaXVzID0gNAogICAgKV0KICAgICMgbWFyayB0aGUgZW5kIG9mIHRoZSB0cmlwCiAgICBkYXRhUGxvdFsuTiwgYDo9YCgKICAgICAgY29sb3IgPSAicmVkIiwKICAgICAgcmFkaXVzID0gNAogICAgKV0KICAgICMgcmVvcmRlciB0byBtYWtlIHRoZSBlbmQgYW5kIHRoZSBiZWdpbm5pbmcgaW4gZnJvbnQKICAgIGRhdGFQbG90IDwtIHJiaW5kKGRhdGFQbG90Wy0xLCBdLCBkYXRhUGxvdFsxLCBdKQoKICAgICMgY29udmVydCB0byBzZgogICAgZGF0YVBsb3QgPC0gc2Y6OnN0X2FzX3NmKGRhdGFQbG90LCBjb29yZHMgPSBjKCJsb24iLCAibGF0IiksIGNycyA9IDQzMjYpCgogICAgIyBhZGQgbWFya2VycyB0byBtYXAKICAgIGdyYWRpZW50X21hcCA8LSBhZGRHbFBvaW50cygKICAgICAgbWFwID0gZ3JhZGllbnRfbWFwLAogICAgICBkYXRhID0gZGF0YVBsb3QsCiAgICAgIHJhZGl1cyA9IGRhdGFQbG90JHJhZGl1cyAqIDQsCiAgICAgIHN0cm9rZSA9IEZBTFNFLAogICAgICBmaWxsQ29sb3IgPSB+Y29sb3IsCiAgICAgIGdyb3VwID0gcGFzdGUoaSwgIi0iLCBrKQogICAgKSAlPiUKICAgICAgYWRkTGVnZW5kKCJib3R0b21sZWZ0IiwKICAgICAgICBkYXRhID0gZGF0YVBsb3QsCiAgICAgICAgZ3JvdXAgPSBwYXN0ZShpLCAiLSIsIGspLAogICAgICAgIHBhbCA9IGNvbFBhbCwKICAgICAgICB2YWx1ZXMgPSB+IGdldChrKSwKICAgICAgICB0aXRsZSA9IGsKICAgICAgKQogICAgIyByZXRyaWV2ZSBncm91cHMKICAgIGdycHMgPC0gYyhncnBzLCBwYXN0ZShpLCAiLSIsIGspKQogIH0KfQoKIyBhZGQgbGF5ZXIgY29udHJvbApncmFkaWVudF9tYXAgPC0gYWRkTGF5ZXJzQ29udHJvbCgKICBtYXAgPSBncmFkaWVudF9tYXAsCiAgb3ZlcmxheUdyb3VwcyA9IGdycHMsCiAgb3B0aW9ucyA9IGxheWVyc0NvbnRyb2xPcHRpb25zKGNvbGxhcHNlZCA9IFRSVUUpCikgJT4lIGhpZGVHcm91cChncnBzKQpgYGAKCmBgYHtyIGRhdGEtZXhwbG9yYXRpb24tMjAxOC00NSwgZmlnLmNhcD0iVHJhY2tpbmcgZGF0YSAyMDE4IGluZGl2aWR1YWxzIChncmVlbiBhbmQgcmVkIGRvdCByZXNwZWN0aXZlbHkgaW5kaWNhdGUgdGhlIGJlZ2lubmluZyBhbmQgdGhlIGVuZCBvZiBlYWNoIHRyaXApIn0KIyBkaXNwbGF5CmdyYWRpZW50X21hcApgYGAKCmBgYHtyfQojIGNsZWFyIG1lbW9yeQpnYygpCnJtKAogIGdyYWRpZW50X21hcCwKICBkYXRhUGxvdAopCmdjKCkKYGBgCgpgYGB7ciBmaXRfZm9pZV9ncmFzLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQp4IDwtIGRhdGFfMjAxOF9maWx0ZXIKeFssIGxjIDo9ICJHIl0KZml0IDwtIGZpdF9zc20oeFssIC4oLmlkLCBkYXRlLCBsYywgbG9uLCBsYXQpXSwgbW9kZWwgPSAicnciLCB0aW1lLnN0ZXAgPSAyNCkKYGBgCgojIyMgQ3VycmVudCBWZWxvY2l0eSBNYXAKCkZpcnN0IHdlIG5lZWQgdG8gbG9hZCBjdXJyZW50IGRhdGEuIEhlcmUgd2UgbG9hZGVkIGRhdGEgZnJvbSBbQ29wZXJuaWN1cyBwbGF0Zm9ybV0oaHR0cHM6Ly9tYXJpbmUuY29wZXJuaWN1cy5ldS8pIHVzaW5nIHRoZSBwcm9kdWN0IFsqKkdsb2JhbCBPY2VhbiBQaHlzaWNzIFJlYW5hbHlzaXMqKl0oaHR0cHM6Ly9yZXNvdXJjZXMubWFyaW5lLmNvcGVybmljdXMuZXUvcHJvZHVjdC1kZXRhaWwvR0xPQkFMX01VTFRJWUVBUl9QSFlfMDAxXzAzMC9JTkZPUk1BVElPTikgdGhhdCBhbGxvdyB0byBnZXQgYW4gZXN0aW1hdGlvbiBvZiB0aGUgY3VycmVudCB2ZWxvY2l0eSBhbmQgZWRkaWVzIGF0IHRoZSBzdXJmYWNlIGF0IGEgc3BhdGlhbCByZXNvbHV0aW9uIG9mIDAuMDgzwrAgw5cgMC4wODPCsCBldmVyeSBkYXkuCgo+IFRoaXMgc3RlcCBtaWdodCB0YWtlIHRpbWUgc2luY2UgdGhhdCByZXByZXNlbnRzIGEgbG90IG9mIGRhdGEuCgpgYGB7ciBsb2FkX2N1cnJlbnQsIGV2YWwgPSBGQUxTRX0KIyBpbXBvcnQgdGhlIGFscmVhZHkgcHJlLXRyZWF0ZWQgbmNkZgpkYXRhKCJkYXRhX2NvcCIsIHBhY2thZ2UgPSAib250b2RpdmUiKQpgYGAKClRoZW4gd2UgY2FuIGJ1aWxkIHRoZSBhbmltYXRpb24gZm9yIGVhY2ggaW5kaXZpZHVhbC4gVGhlIGNvZGUgYmVsb3cgaXMgbm90IGV4ZWN1dGVkIHdoZW4gdGhpcyBkb2N1bWVudCBpcyBjb21waWxlZCwgc2luY2UgaXQncyBxdWl0ZSB0aW1lIGNvbnN1bWluZy4KCmBgYHtyIGRhdGEtZXhwbG9yYXRpb24tMjAxOC0xLWJpcywgZXZhbD1GQUxTRX0KIyBlYXNpZXIgKGl0J3MgYWxzbyBiZWNhdXNlIGl0IHdhcyB0aGUgb25seSB3YXkgdGhhdCB3b3JrcykgdXNpbmcgYSBmdW5jdGlvbgphbmltX3Bsb3RfY3VycmVudCA8LSBmdW5jdGlvbihkYXRhLCBpZF9pbnRlcikgewogICMgcGxvdAogIGdncGxvdCgpICsKICAgIGdlb21fcmFzdGVyKAogICAgICBkYXRhID0gZGF0YV9jb3BbdGltZSAlYmV0d2VlbiUgYygKICAgICAgICBkYXRhWwogICAgICAgICAgLmlkID09IGlkX2ludGVyLAogICAgICAgICAgbWluKGFzLkRhdGUoZGF0ZSkpCiAgICAgICAgXSwKICAgICAgICBkYXRhWwogICAgICAgICAgLmlkID09IGlkX2ludGVyLAogICAgICAgICAgbWF4KGFzLkRhdGUoZGF0ZSkpCiAgICAgICAgXQogICAgICApICYKICAgICAgICBsb25naXR1ZGUgJWJldHdlZW4lIGMoCiAgICAgICAgICBkYXRhWwogICAgICAgICAgICAuaWQgPT0gaWRfaW50ZXIsCiAgICAgICAgICAgIG1pbihsb24pCiAgICAgICAgICBdIC0gMiwKICAgICAgICAgIGRhdGFbCiAgICAgICAgICAgIC5pZCA9PSBpZF9pbnRlciwKICAgICAgICAgICAgbWF4KGxvbikKICAgICAgICAgIF0gKyAyCiAgICAgICAgKSAmCiAgICAgICAgbGF0aXR1ZGUgJWJldHdlZW4lIGMoCiAgICAgICAgICBkYXRhWwogICAgICAgICAgICAuaWQgPT0gaWRfaW50ZXIsCiAgICAgICAgICAgIG1pbihsYXQpCiAgICAgICAgICBdIC0gMiwKICAgICAgICAgIGRhdGFbCiAgICAgICAgICAgIC5pZCA9PSBpZF9pbnRlciwKICAgICAgICAgICAgbWF4KGxhdCkKICAgICAgICAgIF0gKyAyCiAgICAgICAgKV0sCiAgICAgIGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUsIGZpbGwgPSB2ZWwpLAogICAgICBpbnRlcnBvbGF0ZSA9IFRSVUUKICAgICkgKwogICAgZ2VvbV9wb2ludCgKICAgICAgZGF0YSA9IHVuaXF1ZShkYXRhWwogICAgICAgIC5pZCA9PSBpZF9pbnRlciwKICAgICAgICAuKAogICAgICAgICAgdGltZSA9IGFzLkRhdGUoZGF0ZSksCiAgICAgICAgICBsYXQsCiAgICAgICAgICBsb24KICAgICAgICApCiAgICAgIF0pLAogICAgICBhZXMoeCA9IGxvbiwgeSA9IGxhdCksCiAgICAgIGNvbG9yID0gIndoaXRlIgogICAgKSArCiAgICBnZW9tX3NmKAogICAgICBkYXRhID0gc3BEYXRhOjp3b3JsZCwKICAgICAgY29sID0gMSwKICAgICAgZmlsbCA9ICJpdm9yeSIKICAgICkgKwogICAgY29vcmRfc2YoCiAgICAgIHhsaW0gPSBjKAogICAgICAgIGRhdGFbLmlkID09IGlkX2ludGVyLCBtaW4obG9uKV0gLSAyLAogICAgICAgIGRhdGFbLmlkID09IGlkX2ludGVyLCBtYXgobG9uKV0gKyAyCiAgICAgICksCiAgICAgIHlsaW0gPSBjKAogICAgICAgIGRhdGFbLmlkID09IGlkX2ludGVyLCBtaW4obGF0KV0gLSAyLAogICAgICAgIGRhdGFbLmlkID09IGlkX2ludGVyLCBtYXgobGF0KV0gKyAyCiAgICAgICkKICAgICkgKwogICAgdGhlbWVfampvKCkgKwogICAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwpICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKAogICAgICBjb2xvdXJzID0gb2NlOjpvY2VDb2xvcnM5QSgxMjApLAogICAgICBsaW1pdHMgPSBjKDAsIDEpCiAgICApICsKICAgIGxhYnModGl0bGUgPSBwYXN0ZShpZF9pbnRlciwgIi0gRGF0ZToge2ZyYW1lX3RpbWV9IikpICsKICAgIHRyYW5zaXRpb25fdGltZSh0aW1lKQp9CgojIGFwcGx5IHRoaXMgZnVuY3Rpb24gdG8gYWxsIGluZGl2aWR1YWxzCnJlc19nZ3Bsb3QgPC0gbGFwcGx5KGRhdGFfMjAxOF9maWx0ZXJbIWlzLm5hKGxhdCksIHVuaXF1ZSguaWQpXSwKICBhbmltX3Bsb3RfY3VycmVudCwKICBkYXRhID0gZGF0YV8yMDE4X2ZpbHRlcgopCgojIGFwcGx5IHRoZSBhbmltYXRpb24gZm9yIGVhY2ggaW5kaXZpZHVhbApyZXNfZ2dhbmltYXRlIDwtIGxhcHBseShyZXNfZ2dwbG90LCBmdW5jdGlvbih4KSB7CiAgYW5pbWF0ZSh4LCByZW5kZXJlciA9IG1hZ2lja19yZW5kZXJlcigpKQp9KQoKIyB0aGVuIHRvIGRpc3BsYXkgdGhlIGZpcnN0IGluZGl2aWR1YWxzCnJlc19nZ2FuaW1hdGVbWzFdXQoKIyBzYXZlIHRoZSBwbG90CmFuaW1fc2F2ZSgiaW5kXzIwMTgwNzBfdmVsX2FsbHRyaXAuZ2lmIiwgYW5pbWF0aW9uID0gbGFzdF9hbmltYXRpb24oKSkKYGBgCgo8IS0tIGZvciBmIGluICouZ2lmOyAgLS0+CjwhLS0gZG8gIC0tPgo8IS0tICAgICBmZm1wZWcgLWkgIiRmIiAtbW92ZmxhZ3MgZmFzdHN0YXJ0IC1waXhfZm10IHl1djQyMHAgLXZmICJzY2FsZT10IC0tPgo8IS0tIHJ1bmMoaXcvMikqMjp0cnVuYyhpaC8yKSoyIiAiJHtmJS5naWZ9Ii5tcDQ7IC0tPgo8IS0tIGRvbmUgLS0+Cgo8cCBhbGlnbj0iY2VudGVyIj4KIVtpbmRfMjAxODA3MCB0cmFjayB3aXRoIGN1cnJlbnQgdmVsb2NpdHldKGluZF8yMDE4MDcwX3ZlbF9hbGx0cmlwLm1wNCkKPC9wPgoKPHAgYWxpZ249ImNlbnRlciI+CiFbaW5kXzIwMTgwNzIgdHJhY2sgd2l0aCBjdXJyZW50IHZlbG9jaXR5XShpbmRfMjAxODA3Ml92ZWxfYWxsdHJpcC5tcDQpCjwvcD4KCjxwIGFsaWduPSJjZW50ZXIiPgohW2luZF8yMDE4MDgwIHRyYWNrIHdpdGggY3VycmVudCB2ZWxvY2l0eV0oaW5kXzIwMTgwODBfdmVsX2FsbHRyaXAubXA0KQo8L3A+CgojIyMgV2F0ZXIgSGVpZ2h0IE1hcAoKQW5vdGhlciBhcHByb2FjaCBpcyB0byBsb29rIGF0IHRoYXQgd2l0aCBhIHZpZXcgY2VudGVyZWQgb24gdGhlIGFuaW1hbCAoSSBmaW5kIGl0IGVhc2llciB0byBzcG90IGFueSByZWxhdGlvbiB3aXRoIHRoZSBhbmltYWwncyB0cmFjayBhbmQgZW52aXJvbm1lbnRhbCBjb25kaXRpb25zKS4gTGV0J3MgaGF2ZSBhIGxvb2sgYXQgYW5vdGhlciB2YXJpYWJsZSwgdGhlIHNlYSBzdXJmYWNlIGFib3ZlIGdlb2lkIChvZnRlbiBjYWxsZWQgU1NIIGZvciAqKlMqKmVhICoqUyoqdXJmYWNlICoqSCoqZWlnaHQsIGJ1dCBjYWxsZWQgYHpvc2AgaGVyZWFmdGVyKSwgdGhhdCBjYW4gYmUgdXNlZCB0byBbaWRlbnRpZnkgZWRkaWVzXShodHRwczovL3d3dy5yZXNlYXJjaGdhdGUubmV0L2ZpZ3VyZS9FZGR5LWRlZmluaXRpb24tdXNpbmctdGhlLWxlZnQtU1NILWFuZC1yaWdodC1RLW1ldGhvZHMtZm9yLWEtY3ljbG9uaWMtZWRkeS1hdF9maWc0XzI0Nzc3ODAwMSkuCgpgYGB7ciBkYXRhLWV4cGxvcmF0aW9uLTIwMTgtNi1iaXMsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMgYCB0aGlzIGNvZGUgYWxsb3cgdG8gdXNlIHRoZSBmdW5jdGlvbiB2aWV3X2ZvbGxvdyB3aXRoIGdnYW5pbWF0ZSwgYW5kCiMgYCBzcGVjaWZ5aW5nIGF0IHRoZSBzYW1lIHRpbWUgd2hpY2ggbGF5ZXIgdG8gbm90IGZvbGxvdwp2aWV3X2ZvbGxvdyA8LQogIGZ1bmN0aW9uKGZpeGVkX3ggPSBGQUxTRSwKICAgICAgICAgICBmaXhlZF95ID0gRkFMU0UsCiAgICAgICAgICAgZXhjbHVkZV9sYXllciA9IE5VTEwsCiAgICAgICAgICAgYXNwZWN0X3JhdGlvID0gMSkgewogICAgZ2dwcm90bygKICAgICAgTlVMTCwKICAgICAgVmlld0ZvbGxvdywKICAgICAgZXhjbHVkZV9sYXllciA9IGV4Y2x1ZGVfbGF5ZXIsCiAgICAgIGFzcGVjdF9yYXRpbyA9IGFzcGVjdF9yYXRpbywKICAgICAgZml4ZWRfbGltID0gbGlzdCh4ID0gZml4ZWRfeCwgeSA9IGZpeGVkX3kpCiAgICApCiAgfQoKVmlld0ZvbGxvdyA8LSBnZ3Byb3RvKAogICJWaWV3Rm9sbG93IiwKICBWaWV3LAogIHNldF92aWV3ID0gZnVuY3Rpb24oc2VsZiwgcGxvdCwgcGFyYW1zLCBpKSB7CiAgICBpZiAoaW5oZXJpdHMocGxvdCRsYXlvdXQkY29vcmQsICJDb29yZFBvbGFyIikpIHsKICAgICAgc3RvcCgiVGhpcyB2aWV3IGRvZXMgbm90IHN1cHBvcnQgcG9sYXIgY29vcmRpbmF0ZXMiKQogICAgfQogICAgcmFuZ2VzIDwtIHNlbGYkZ2V0X3JhbmdlcyhwbG90JGRhdGEsIHBhcmFtcykKICAgIHJhbmdlcyA8LQogICAgICByYW5nZXNbIXNlcV9hbG9uZyhyYW5nZXMpICVpbiUgcGFyYW1zJGV4Y2x1ZGVkX2xheWVyXQogICAgeF9yYW5nZSA8LQogICAgICByYW5nZShpbmYub21pdCh1bmxpc3QobGFwcGx5KHJhbmdlcywgYFtbYCwgIngiKSkpKQogICAgeV9yYW5nZSA8LQogICAgICByYW5nZShpbmYub21pdCh1bmxpc3QobGFwcGx5KHJhbmdlcywgYFtbYCwgInkiKSkpKQogICAgaWYgKCFpcy5udWxsKHBsb3QkbGF5b3V0JHBhbmVsX3NjYWxlc194W1sxXV0kdHJhbnMpKSB7CiAgICAgIHhfcmFuZ2UgPC0gcGxvdCRsYXlvdXQkcGFuZWxfc2NhbGVzX3hbWzFdXSR0cmFucyRpbnZlcnNlKHhfcmFuZ2UpCiAgICB9CiAgICBpZiAoIWlzLm51bGwocGxvdCRsYXlvdXQkcGFuZWxfc2NhbGVzX3lbWzFdXSR0cmFucykpIHsKICAgICAgeV9yYW5nZSA8LSBwbG90JGxheW91dCRwYW5lbF9zY2FsZXNfeVtbMV1dJHRyYW5zJGludmVyc2UoeV9yYW5nZSkKICAgIH0KCiAgICBzZWxmJHJlc2V0X2xpbWl0cyhwbG90LCB4X3JhbmdlLCB5X3JhbmdlKQogIH0KKQoKaW5mLm9taXQgPC0gZnVuY3Rpb24oeCkgewogIHhbaXMuZmluaXRlKHgpXQp9CmBgYAoKU2FtZSBhcyBhYm92ZSwgc2luY2UgdGhpcyBzdGVwIGlzIHRpbWUtY29uc3VtaW5nLCB0aGUgZm9sbG93aW5nIGNvZGUgd2lsbCBub3QgYmUgcnVuLCBidXQgZ2l2ZXMgeW91IGFuIGlkZWEgb2YgaG93IHRvIGdlbmVyYXRlIGAqLmdpZmAuCgpgYGB7ciBkYXRhLWV4cGxvcmF0aW9uLTIwMTgtNy1iaXMsIGV2YWw9RkFMU0V9CiMgZ2V0IHRoZSBwb3NpdGlvbiBvZiB0aGUgYW5pbWFsIGVhY2ggZGF5Cmdwc19kYXkgPC0gZGF0YV8yMDE4X2ZpbHRlclshaXMubmEobGF0KSwgLihkYXRlLCBsYXQsIGxvbiwgLmlkKV0gJT4lCiAgLlssIC4oCiAgICBsYXQgPSBtZWFuKGxhdCwgbmEucm0gPSBUKSwKICAgIGxvbiA9IG1lYW4obG9uLCBuYS5ybSA9IFQpCiAgKSwKICBieSA9IC4oLmlkLCB0aW1lID0gYXMuRGF0ZShkYXRlKSkKICBdCgphbmltX3Bsb3Rfem9zX2NlbnRlciA8LSBmdW5jdGlvbihpZF9pbnRlcikgewogICMgaW5pdGlhdGUKICBkZl9yYXN0ZXJfaW50ZXIgPC0gZGF0YS50YWJsZSgpCgogICMgZXhhbXBsZSB3aXRoIGlkX2ludGVyCiAgZm9yIChpIGluIDE6Z3BzX2RheVsuaWQgPT0gaWRfaW50ZXIsIC5OXSkgewogICAgIyByZXRyaWV2ZSB0aGUgcmlnaHQgdmFsdWVzCiAgICB0aW1lX2ludGVyIDwtIGdwc19kYXlbLmlkID09IGlkX2ludGVyLCB0aW1lXVtpXQogICAgbGF0X2ludGVyIDwtIGdwc19kYXlbLmlkID09IGlkX2ludGVyLCBsYXRdW2ldCiAgICBsb25faW50ZXIgPC0gZ3BzX2RheVsuaWQgPT0gaWRfaW50ZXIsIGxvbl1baV0KCiAgICAjIGdldCB0aGUgcmlnaHQgZGF0YQogICAgZGZfcmFzdGVyX2ludGVyIDwtIHJiaW5kKAogICAgICBkZl9yYXN0ZXJfaW50ZXIsCiAgICAgIGRhdGFfY29wW3RpbWUgPT0gdGltZV9pbnRlciAmCiAgICAgICAgbGF0aXR1ZGUgJWJldHdlZW4lIGMoCiAgICAgICAgICBsYXRfaW50ZXIgLSA0LAogICAgICAgICAgbGF0X2ludGVyICsgNAogICAgICAgICkgJgogICAgICAgIGxvbmdpdHVkZSAlYmV0d2VlbiUgYygKICAgICAgICAgIGxvbl9pbnRlciAtIDQsCiAgICAgICAgICBsb25faW50ZXIgKyA0CiAgICAgICAgKV0KICAgICkKICB9CgogICMgcmVsZWFzZSBtZW1vcnkKICBnYygpCgogICMgcGxvdAogIHBsb3RfYW5pbWF0ZSA8LSBnZ3Bsb3QoKSArCiAgICBnZW9tX3Jhc3RlcigKICAgICAgZGF0YSA9IGRmX3Jhc3Rlcl9pbnRlclssIC4obG9uZ2l0dWRlLCBsYXRpdHVkZSwgem9zLCB0aW1lKV0sCiAgICAgIGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUsIGZpbGwgPSB6b3MpCiAgICApICsKICAgIGdlb21fcGF0aCgKICAgICAgZGF0YSA9IHVuaXF1ZShkYXRhXzIwMThfZmlsdGVyWy5pZCA9PSBpZF9pbnRlciwgLihsYXQsIGxvbildKSwKICAgICAgYWVzKHggPSBsb24sIHkgPSBsYXQpLAogICAgICBjb2xvciA9ICJyZWQiLAogICAgICBzaXplID0gMgogICAgKSArCiAgICBnZW9tX3BvaW50KAogICAgICBkYXRhID0gZ3BzX2RheVsuaWQgPT0gaWRfaW50ZXIsIF0sCiAgICAgIGFlcyh4ID0gbG9uLCB5ID0gbGF0KSwKICAgICAgY29sb3IgPSAid2hpdGUiLAogICAgICBzaXplID0gMgogICAgKSArCiAgICB0aGVtZV9qam8oKSArCiAgICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCkgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IG9jZTo6b2NlQ29sb3JzOUEoMTIwKSkgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKGlkX2ludGVyLCAiLSBEYXRlOiB7ZnJhbWVfdGltZX0iKSkgKwogICAgdHJhbnNpdGlvbl90aW1lKHRpbWUpICsKICAgIHZpZXdfZm9sbG93KGV4Y2x1ZGVfbGF5ZXIgPSAyKQoKICAjIHJtCiAgcm0oZGZfcmFzdGVyX2ludGVyKQoKICAjIHJldHVybgogIHJldHVybihwbG90X2FuaW1hdGUpCn0KCiMgYXBwbHkgdGhpcyBmdW5jdGlvbiB0byBhbGwgaW5kaXZpZHVhbHMKcmVzX2dncGxvdF9jZW50ZXIgPC0gbGFwcGx5KAogIGRhdGFfMjAxOF9maWx0ZXJbIWlzLm5hKGxhdCksIHVuaXF1ZSguaWQpXSwKICBhbmltX3Bsb3Rfem9zX2NlbnRlcgopCgojIGFwcGx5IHRoZSBhbmltYXRpb24gZm9yIGVhY2ggaW5kaXZpZHVhbApyZXNfZ2dhbmltYXRlX2NlbnRlciA8LSBsYXBwbHkocmVzX2dncGxvdF9jZW50ZXIsIGZ1bmN0aW9uKHgpIHsKICBhbmltYXRlKHgsIGR1cmF0aW9uID0gMjAsIG5mcmFtZXMgPSAyMDAsIHJlbmRlcmVyID0gbWFnaWNrX3JlbmRlcmVyKCkpCiAgZ2MoKQp9KQoKIyBmaXJzdCBpbmRpdmlkdWFsCnJlc19nZ2FuaW1hdGVfY2VudGVyW1sxXV0KCiMgc2F2ZSBnaWYgZmlsZQphbmltX3NhdmUoImluZF8yMDE4MDcwX3pvc19jZW50ZXIuZ2lmIiwgYW5pbWF0aW9uID0gbGFzdF9hbmltYXRpb24oKSkKYGBgCgpJbiBhZGRpdGlvbiwgdXNpbmcgYGdnYW5pbWF0ZWAgdG8gZ2VuZXJhdGUgYCouZ2lmYCBmaWxlIHdhcyBmb3VuZCB0byBiZSBtZW1vcnkgY29uc3VtaW5nIHNvIHdlIGhhZCB0byBkZXZlbG9wZWQgZm9yIHNvbWUgaW5kaXZpZHVhbHMgYW5vdGhlciB3YXkgdG8gZ2VuZXJhdGUgYCouZ2lmYCBmaWxlLiBIZXJlIHdlIHVzZWQgdGhlICJvbGQtZmFzaGlvbmVkIiB3YXkgY29uc2lzdGluZyBpbiBnZW5lcmF0aW5nIGFsbCB0aGUgcGxvdHMgYW5kIHRoZW4gY29tcGlsZSB0aGVtIGludG8gYSBgKi5naWZgIGZpbGUuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsID0gRkFMU0V9CmFub3RoZXJfYW5pbV9wbG90X3pvc19jZW50ZXIgPC0gZnVuY3Rpb24oaWRfaW50ZXIpIHsKICAjIGV4YW1wbGUgd2l0aCBpZF9pbnRlcgogIGZvciAoaSBpbiAxOmdwc19kYXlbLmlkID09IGlkX2ludGVyLCAuTl0pIHsKICAgICMgcmV0cmlldmUgdGhlIHJpZ2h0IHZhbHVlcwogICAgdGltZV9pbnRlciA8LSBncHNfZGF5Wy5pZCA9PSBpZF9pbnRlciwgdGltZV1baV0KICAgIGxhdF9pbnRlciA8LSBncHNfZGF5Wy5pZCA9PSBpZF9pbnRlciwgbGF0XVtpXQogICAgbG9uX2ludGVyIDwtIGdwc19kYXlbLmlkID09IGlkX2ludGVyLCBsb25dW2ldCgogICAgIyBnZXQgdGhlIHJpZ2h0IGRhdGEKICAgIGRmX3Jhc3Rlcl9pbnRlciA8LSBkYXRhX2NvcFt0aW1lID09IHRpbWVfaW50ZXIgJgogICAgICBsYXRpdHVkZSAlYmV0d2VlbiUgYygKICAgICAgICBsYXRfaW50ZXIgLSA0LAogICAgICAgIGxhdF9pbnRlciArIDQKICAgICAgKSAmCiAgICAgIGxvbmdpdHVkZSAlYmV0d2VlbiUgYygKICAgICAgICBsb25faW50ZXIgLSA0LAogICAgICAgIGxvbl9pbnRlciArIDQKICAgICAgKV0KICAgICMgcGxvdAogICAgcCA8LSBnZ3Bsb3QoKSArCiAgICAgIGdlb21fdGlsZSgKICAgICAgICBkYXRhID0gZGZfcmFzdGVyX2ludGVyWywgLihsb25naXR1ZGUsIGxhdGl0dWRlLCB6b3MsIHRpbWUpXSwKICAgICAgICBhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBmaWxsID0gem9zKQogICAgICApICsKICAgICAgZ2VvbV9wYXRoKAogICAgICAgIGRhdGEgPSB1bmlxdWUoZGF0YV8yMDE4X2ZpbHRlclsuaWQgPT0gaWRfaW50ZXIsIC4obGF0LCBsb24pXSksCiAgICAgICAgYWVzKHggPSBsb24sIHkgPSBsYXQpLAogICAgICAgIGNvbG9yID0gInJlZCIsCiAgICAgICAgc2l6ZSA9IDEuNQogICAgICApICsKICAgICAgZ2VvbV9wb2ludCgKICAgICAgICBkYXRhID0gZ3BzX2RheVsuaWQgPT0gaWRfaW50ZXIsIF1baSwgXSwKICAgICAgICBhZXMoeCA9IGxvbiwgeSA9IGxhdCksCiAgICAgICAgY29sb3IgPSAid2hpdGUiLAogICAgICAgIHNpemUgPSAyCiAgICAgICkgKwogICAgICB0aGVtZV9qam8oKSArCiAgICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKSArCiAgICAgIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMKSArCiAgICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSBvY2U6Om9jZUNvbG9yczlBKDEyMCkpICsKICAgICAgbGFicyh0aXRsZSA9IHBhc3RlKGlkX2ludGVyLCAiLSBEYXRlOiAiLCB0aW1lX2ludGVyKSkgKwogICAgICBjb29yZF9jYXJ0ZXNpYW4oCiAgICAgICAgeWxpbSA9IGMobGF0X2ludGVyIC0gNCwgbGF0X2ludGVyICsgNCksCiAgICAgICAgeGxpbSA9IGMobG9uX2ludGVyIC0gNCwgbG9uX2ludGVyICsgNCkKICAgICAgKQoKICAgICMgc2F2ZQogICAgZ2dzYXZlKAogICAgICBwbG90ID0gcCwKICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAoIi4vdG1wLyIsIGlkX2ludGVyLCAiIC0gRGF0ZTogIiwgdGltZV9pbnRlciwgIi5wbmciKSwKICAgICAgZGV2aWNlID0gInBuZyIKICAgICkKICB9Cn0KCiMgcnVuIHRoZSBmdW5jdGlvbiBmb3Igb25lIGluZGl2aWR1YWwKYW5vdGhlcl9hbmltX3Bsb3Rfem9zX2NlbnRlcigiaW5kXzIwMTgwNzIiKQoKIyBjb21waWxlIHRoZSBnaWYgZmlsZQpnaWZza2koCiAgbGlzdC5maWxlcygidG1wLyIsIHBhdHRlcm4gPSAicG5nJCIsIGZ1bGwubmFtZXMgPSBUKSwKICBnaWZfZmlsZSA9ICJpbmRfMjAxODA3Ml96b3NfY2VudGVyLmdpZiIsCiAgd2lkdGggPSA0ODAsCiAgaGVpZ2h0ID0gNDgwLAogIGRlbGF5ID0gMC4xCikKYGBgCgo8cCBhbGlnbj0iY2VudGVyIj4KIVtpbmRfMjAxODA3MCBjZW50ZXJlZC10cmFjayB3aXRoIHNlYSBzdXJmYWNlIGhlaWdodF0oaW5kXzIwMTgwNzBfem9zX2NlbnRlci5tcDQpCjwvcD4KCjxwIGFsaWduPSJjZW50ZXIiPgohW2luZF8yMDE4MDcyIGNlbnRlcmVkLXRyYWNrIHdpdGggc2VhIHN1cmZhY2UgaGVpZ2h0XShpbmRfMjAxODA3Ml96b3NfY2VudGVyLm1wNCkKPC9wPgoKPHAgYWxpZ249ImNlbnRlciI+CiFbaW5kXzIwMTgwODAgY2VudGVyZWQtdHJhY2sgd2l0aCBzZWEgc3VyZmFjZSBoZWlnaHRdKGluZF8yMDE4MDgwX3pvc19jZW50ZXIubXA0KQo8L3A+CgpIYXJkIHRvIHRlbGwgaWYgdGhlcmUgaXMgYW55IHJlbGF0aW9uLCB3ZSBtaWdodCBuZWVkIHRvIGRpZyBkZWVwZXIsIGVzcGVjaWFsbHkgZm9yIGBpbmRfMjAxODA3MmAgYW5kIGBpbmRfMjAxODA4MGAgYnkgbG9va2luZyBhdCB0aGUgY3VycmVudCBkaXJlY3Rpb24gYW5kL29yIHRoZSBkaXN0cmlidXRpb24gYW5kIGFidW5kYW5jZSBvZiBkaWZmZXJlbnQgdHJvcGhpYyBsZXZlbCB1c2luZyBbU0VBUE9EWU1dKGh0dHA6Ly93d3cuc2VhcG9keW0uZXUvKS4KCiMjIEFsb25nIHRoZWlyIHRyaXAKCmBgYHtyIGZpZy5jYXA9IkV2b2x1dGlvbiBvZiBvY2Vhbm9ncmFwaGljIGRhdGEgd2l0aCB0aGUgbnVtYmVyIG9mIGRheXMgc2luY2UgZGVwYXJ0dXJlIn0KIyBldm9sdXRpb24gd2l0aCB0cmlwIGF0IHNlYQpnZ3Bsb3QoCiAgbWVsdCgKICAgIGRhdGFfMjAxOF9maWx0ZXIsCiAgICBpZC52YXJzID0gYygiLmlkIiwgImRheV9kZXBhcnR1cmUiKSwKICAgIG1lYXN1cmUudmFycyA9IGMoInRlbXAiLCAic3NoIiwgInBzdSIsICJ2ZWwiKQogICksCiAgYWVzKAogICAgeCA9IGRheV9kZXBhcnR1cmUsCiAgICB5ID0gdmFsdWUsCiAgICBjb2wgPSAuaWQKICApCikgKwogIGdlb21fbGluZSgpICsKICBmYWNldF93cmFwKHZhcmlhYmxlIH4gLiwgc2NhbGVzID0gImZyZWUiKSArCiAgdGhlbWVfampvKCkgKwogIGxhYnMoeSA9ICJWYWx1ZXMiLCB4ID0gIiMgb2YgZGF5cyBzaW5jZSBkZXBhcnR1cmUiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmBgYAo=